/* 	Authors: Ha Le Hoai Trung
	Date: 15/11/2007
*/
package MP.staticchecker;

import MP.CompilationException;
import MP.ASTs.*;
import MP.lexicalanalysis.Token;
import java.util.*;

public class StaticChecker {
	AST astTree;
	SymTable sym;

	public StaticChecker(AST ast) {
		astTree = ast;
		sym = new SymHashTable();
	}

	public void parse() throws CompilationException {
		Visitor ph = new DivideByZeroCheck();
		astTree.visit(ph,null);		
	}

}


// To check divide-by-zero error.
class DivideByZeroCheck implements Visitor {
	SymHashTable sym;
	ArrayList arr;
	private int i ;
	private int flag;//Co bao hieu vao proc 
	// 1 vao lay tham so 
	// 2 ngan kieu Prim Type vao array
	private int lArray;//lay gia tri element ben trai de kiem tra Lvalue
	private int flaga;//Bao hieu up > low trong array
	private int flagLoop;//Bao hieu vao vong lap
	private int flagScope;
	Type tyLiter;// set khai bao Type khi gap Literal
	private int flagNest;// dung de bao goi ham long nhau
	LeftMostVisitor leftToken;
	int func;
	int inFunc;
	int left;
	
	//ham dung
	DivideByZeroCheck()
	{
		sym = new SymHashTable();
		i = 0;
		arr = new ArrayList();
		tyLiter = null;
		lArray = 0;
		flaga = 0;
		flagScope = 0;
		flagNest = 0;
		leftToken = new LeftMostVisitor();
		func = 0;
		inFunc = 0;
		left = 0;
	}
	
	// ProgramAST
	public Object visitProgramAST(ProgramAST ast, Object o)
			throws CompilationException {
		//sym.enterScope();				
		ast.v.visit(this,o);		
		ast.p.visit(this, o);
		func = 1;
		ast.c.visit(this, o);
		func = 0;
		sym.exitScope();
		return null;
	}
	
	//VarDeclPar
	public Object visitVarDeclPartAST(VarDeclPartAST ast, Object o)
			throws CompilationException {
		ast.o.visit(this,o);
		ast.v.visit(this,o);		
		return null;
	}

	// EmptyVarDeclPartAST
	public Object visitEmptyVarDeclPartAST(EmptyVarDeclPartAST ast, Object o)
			throws CompilationException {
		return null;
	}

	// VarDeclAST
	public Object visitVarDeclAST(OneDeclAST ast, Object o)
			throws CompilationException {
		VarDeclAST vast = (VarDeclAST) ast;
		
		Type typevar = (Type) vast.t.visit(this,o);
		SymHashEntry temp = new SymHashEntry(vast.name,typevar);
		SymHashEntry entryIn =(SymHashEntry) sym.insertId(temp);
		
	/*	System.out.println("" + sym.getScope(vast.name.Lexeme));
		System.out.println("" + sym.currentScope);*/
		if(entryIn != null)
		{
			throw new RedeclaredVariableException(vast.name);
		}
		
		if(flaga == 1)
		{
			flaga = 0;
			throw new ArrayRangeException(ast.name);
		}
				
		return null;
	}

	// ConstDeclAST
	public Object visitConstDeclAST(OneDeclAST ast, Object o)
			throws CompilationException {
		ConstDeclAST vast = (ConstDeclAST) ast;
		vast.l.visit(this,o);
		
		String strliteral = "" + vast.l.visit(this,o);		
		SymHashEntry entryIn =(SymHashEntry) sym.insertId
			(new SymHashEntry(vast.name,(Type) new ConstType((Type) tyLiter),strliteral));
		tyLiter = null;//reset Type cua class 
				
		if(entryIn != null)
		{
			throw new RedeclaredVariableException(vast.name);
		}
				
		return null;
	}

	// ArrayTypeAST
	public Object visitArrayTypeAST(ArrayTypeAST ast, Object o)
			throws CompilationException {
		int low = Integer.valueOf(ast.lower.Lexeme);
		int up = Integer.valueOf(ast.upper.Lexeme);
		RangeType rat = new RangeType(up,low);
		if(flag == 1)//Xet co phai la dang them vao para
		{
			flag = 2;//ngan them kieu Prim vao Array
		}		
		Type typ = (Type) ast.type.visit(this,o);		
		if(flag == 1)
		{
			arr.add(i,new ArrayType((Type) rat, (Type) typ));
			i++;
		}
		
		if(Integer.parseInt(ast.lower.Lexeme) > Integer.parseInt(ast.upper.Lexeme))
		{
			flaga = 1;
		}
				
		return new ArrayType((Type) rat, (Type) typ);
	}

	// EmptyExprListAST
	public Object visitEmptyExprListAST(EmptyExprListAST ast, Object o)
			throws CompilationException {
		flag = 0;
		if(i == 0)
			flagNest = 1;
		return null;
	}

	// ExprListAST
	public Object visitExprListAST(ExprListAST ast, Object o)
			throws CompilationException {//chua xu ly kieu
		Object e = ast.e.visit(this, o);		
		Object l = ast.l.visit(this, o);
		if(lArray == 1)
		{
			return e;
		}
		return null;	
	}

	// ProDeclPartAST
	public Object visitProDeclPartAST(ProDeclPartAST ast, Object o)
			throws CompilationException {
		ast.o.visit(this, o);
		ast.p.visit(this, o);
		return null;
	}

	// EmptyProDeclPartAST
	public Object visitEmptyProDeclPartAST(EmptyProDeclPartAST ast, Object o)
			throws CompilationException {
		return null;
	}

	// FuncDeclAST
	public Object visitFuncDeclAST(FuncDeclAST fAst, Object o)
			throws CompilationException {		
		flag = 1;
		FunctionType Ttemp = new FunctionType(null, null);
		SymHashEntry EntryTemp = new SymHashEntry(fAst.name, Ttemp);
		SymHashEntry entryIn = (SymHashEntry) sym.insertId((SymEntry) EntryTemp);//bo vao bang		
		if(entryIn != null)
		{
			throw new RedeclaredProcedureException(fAst.name);
		}
		
		sym.enterScope();
		
		sym.insertId((SymEntry) EntryTemp);
		flagScope = sym.currentScope;
		int tempScope = flagScope;	
			
		fAst.para.visit(this,o);		
		ParaType pa = new ParaType(arr);
		Type tfunc = (Type) fAst.ret.visit(this,o);
		
		EntryTemp.setType(new FunctionType(pa,tfunc));
		//System.out.println(""+ arr.size());
		if(!arr.isEmpty())
		{
			arr.clear();
			i = 0;
		}
				
		fAst.v.visit(this,o);
		fAst.p.visit(this,o);
		
		//vao compound khoi phuc lai scope cua func
		flagScope = tempScope;
		fAst.c.visit(this,o);
		sym.exitScope();
		return null;
	}

	// ProcDeclAST
	public Object visitProcDeclAST(ProcDeclAST pAst, Object o)
			throws CompilationException {		
		flag = 1;
		FunctionType df = new FunctionType(null, null);
		SymHashEntry entryTemp = new SymHashEntry(pAst.name , df);
		
		SymHashEntry entryIn = (SymHashEntry) sym.insertId((SymEntry)entryTemp);//bo vao bang
		if(entryIn != null)
		{
			throw new RedeclaredProcedureException(pAst.name);
		}
		sym.enterScope();
		flagScope = sym.currentScope;
		int tempScope = flagScope;	
						
		pAst.para.visit(this,o);						
		ParaType pa = new ParaType(arr);
						
		entryTemp.setType(new FunctionType (pa,new VoidType()));
			
		
		if (!arr.isEmpty())
		{
			arr.clear();//
			i = 0;
		}
		pAst.v.visit(this,o);
		pAst.p.visit(this,o);
		
		//vao compound phuc hoi lai Scope
		flagScope = tempScope;
		pAst.c.visit(this,o);
		sym.exitScope();
		return null;
	}

	// ParaListAST
	public Object visitParaListAST(ParaListAST pAst, Object o)
			throws CompilationException {
		pAst.v.visit(this,o);
		pAst.p.visit(this,o);
		return null;
	}

	// EmptyParaListAST
	public Object visitEmptyParaListAST(EmptyParaListAST ast, Object o)
			throws CompilationException {
		flag = 0;
		return null;
	}

	// BoolTypeAST
	public Object visitBoolTypeAST(BoolTypeAST ast, Object o)
			throws CompilationException {
		if(flag == 1)
		{
			arr.add(i, new BooleanType());	
			i++;			
		}
		else if(flag == 2)
		{
			flag = 1;//khoi phuc viec nap vao para	
		}			
		return new BooleanType();
	}

	// IntTypeAST
	public Object visitIntTypeAST(IntTypeAST ast, Object o)
			throws CompilationException {
		if(flag == 1)
		{
			arr.add(i, new IntegerType());
			i++;			
		}
		else if(flag == 2)
		{
			flag = 1;//khoi phuc viec nap vao para	
		}
			
		return new IntegerType();
	}

	// RealTypeAST
	public Object visitRealTypeAST(RealTypeAST ast, Object o)
			throws CompilationException {
		if(flag == 1)
		{
			arr.add(i, new RealType());
			i++;			
		}
		else if (flag == 2)
		{
			flag = 1;//khoi phuc viec nap vao para	
		}	
		return new RealType();
	}

	// StmtListAST
	public Object visitStmtListAST(StmtListAST sAst, Object o)
			throws CompilationException {
		sAst.o.visit(this, o);
		sAst.s.visit(this, o);
		return null;
	}

	// EmptyStmtListAST
	public Object visitEmptyStmtListAST(EmptyStmtListAST ast, Object o)
			throws CompilationException {
		return null;
	}

	// AssiStmtAST
	public Object visitAssiStmtAST(AssiStmtAST aAst, Object o)
			throws CompilationException {		
		Object templ = aAst.l.visit(this, o);
		Object tempe = aAst.e.visit(this, o);
	//	System.out.println(" "+ aAst.l.name.Lexeme);
		if(templ instanceof RealType)
		{
			if(!(tempe instanceof NumericType || tempe instanceof Integer))
			{
				throw new TypeMismatchException((Token) leftToken.visitLvalueAST(aAst.l,o));
			}			
		}
		else if(templ instanceof IntegerType)
		{
			if(!(tempe instanceof IntegerType || tempe instanceof Integer))
			{
				throw new TypeMismatchException((Token) leftToken.visitLvalueAST(aAst.l,o));
			}
		}
		else if(templ instanceof BooleanType)
		{
			if(!(tempe instanceof BooleanType))
			{
				throw new TypeMismatchException((Token) leftToken.visitLvalueAST(aAst.l,o));
			}
		}
		return null;
	}

	// IfThenElseStmtAST
	public Object visitIfThenElseStmtAST(IfThenElseStmtAST iAst, Object o)
			throws CompilationException {
		Object condi = iAst.e.visit(this, o);
		if(!(condi instanceof BooleanType))
			throw new IfConditionalException((Token) leftToken.visitIfThenElseStmtAST(iAst,o));
		
		iAst.s1.visit(this, o);
		iAst.s2.visit(this, o);
		return null;
	}

	// IfThenStmtAST
	public Object visitIfThenStmtAST(IfThenStmtAST iAst, Object o)
			throws CompilationException {
		Object condi = iAst.e.visit(this, o);
		if(!(condi instanceof BooleanType))
			throw new IfConditionalException((Token) leftToken.visitIfThenStmtAST(iAst,o));
		iAst.s1.visit(this, o);
		return null;
	}

	// RepeatStmtAST
	public Object visitRepeatStmtAST(RepeatStmtAST rAst, Object o)
			throws CompilationException {
		flagLoop = 1;
		Object condi = rAst.e.visit(this, o);
		if(!(condi instanceof BooleanType))
		{
			if(rAst.e instanceof BinExprAST)
			{
				BinExprAST tempB = (BinExprAST) rAst.e;
				throw new UntilConditionalException
					((Token) leftToken.visitBinExprAST(tempB,o));					
			}
			else if(rAst.e instanceof UnaryExprAST)
			{
				UnaryExprAST tempU = (UnaryExprAST) rAst.e;
				throw new UntilConditionalException(
					(Token) leftToken.visitUnaryExprAST(tempU,o));
			}
			else if(rAst.e instanceof VarExprAST)
			{
				VarExprAST tempV = (VarExprAST) rAst.e;
				throw new UntilConditionalException(
					(Token) leftToken.visitVarExprAST(tempV,o));
			}
			else if(rAst.e instanceof EleExprAST)
			{
				EleExprAST tempE = (EleExprAST) rAst.e;
				throw new UntilConditionalException(
					(Token) leftToken.visitEleExprAST(tempE,o));
			}
			else if(rAst.e instanceof CallExprAST)
			{
				CallExprAST tempC = (CallExprAST) rAst.e;
				throw new UntilConditionalException(
					(Token) leftToken.visitCallExprAST(tempC,o));
			}
		}			
		rAst.s.visit(this, o);
		flagLoop = 0;
		return null;
	}

	// RetNullStmtAST
	public Object visitRetNullStmtAST(RetNullStmtAST ast, Object o)
			throws CompilationException {
		return null;
	}

	// RetStmtAST
	public Object visitRetStmtAST(RetStmtAST rAst, Object o)
			throws CompilationException {
		return null;
	}

	// CallStmtAST
	public Object visitCallStmtAST(CallStmtAST ast, Object o)
			throws CompilationException {
		//System.out.println(ast.name.Lexeme);
		SymEntry idfunc = sym.lookup(ast.name.Lexeme);
		if(idfunc == null)
		{
			throw new UndeclaredProcedureException(ast.name);
		}
		else if(idfunc != null )
		{
			if(idfunc.getType() instanceof ArrayType)
			{
				throw new ReferArrayAsProcException(ast.name);
			}
			else if( !(idfunc.getType() instanceof FunctionType))
			{
				throw new ReferVarAsProcException(ast.name);
			}			
			else 
			{
				flag = 1;
				ast.e.visit(this, o);
				FunctionType func = (FunctionType) idfunc.getType();
				if(!(func.rType instanceof VoidType))
				{
					throw new TooFewArgumentsException(ast.name);
				}
				ParaType pas = (ParaType) func.pType;
				//System.out.println("flag "+flag + arr.size() +" "+ pas.pa.size());
												
				if(arr.size() > pas.pa.size())
				{
					throw new TooManyArgumentsException(ast.name);
				}
				else if((arr.size() < pas.pa.size()) || !(func.rType instanceof VoidType))
				{
					throw new TooFewArgumentsException(ast.name);
				}
				ParaType paCall = new ParaType(arr);
				if(pas.isLeftCompatibleTo(paCall) == false)
					throw new TypeMismatchException((Token) leftToken.visitExprListAST(ast.e,o));
				if(!arr.isEmpty())
				{
					arr.clear();
					i = 0;
				}
			}			
		}		
		return null;
	}

	// ForStmtAST
	public Object visitForStmtAST(ForStmtAST fAst, Object o)
			throws CompilationException {
		SymEntry hashEntry = sym.lookup(fAst.name.Lexeme);
		flagLoop = 1;
		if(hashEntry == null)
		{
			throw new UndeclaredVariableException(fAst.name);			
		}
		else
		{
			if(hashEntry.getType() instanceof ConstType)
				throw new AssignValueToConstantException(fAst.name);
			else if(hashEntry.getType() instanceof FunctionType)
				throw new ReferProcAsVarException(fAst.name);
			else if(hashEntry.getType() instanceof ArrayType)
			{
				throw new ReferArrayAsVarException(fAst.name);
			}
			else if(!(hashEntry.getType() instanceof IntegerType))
				throw new ForConditionalException(fAst.name);			
			else if(!(sym.currentScope == sym.getScope(fAst.name.Lexeme) ||
				sym.getScope(fAst.name.Lexeme) == 0 || 
				sym.getScope(fAst.name.Lexeme) == flagScope))//xet tam vuc
				throw new ForConditionalException(fAst.name);				
		}
		
		Object e1 = fAst.e1.visit(this,o);
		if(!(e1 instanceof IntegerType || e1 instanceof Integer))
		{
			if(fAst.e1 instanceof BinExprAST)
			{
				BinExprAST btemp = (BinExprAST) fAst.e1;
				throw new ForConditionalException((Token) leftToken.visitBinExprAST(btemp,o));
			}
			else if(fAst.e1 instanceof UnaryExprAST)
			{
				UnaryExprAST utemp = (UnaryExprAST) fAst.e1;
				throw new ForConditionalException((Token) leftToken.visitUnaryExprAST(utemp,o));				
			}
			else if(fAst.e1 instanceof VarExprAST)
			{
				VarExprAST vtemp = (VarExprAST) fAst.e1;
				throw new ForConditionalException((Token) leftToken.visitVarExprAST(vtemp,o));	
			}
			else if(fAst.e1 instanceof EleExprAST)
			{
				EleExprAST etemp = (EleExprAST) fAst.e1;
				throw new ForConditionalException((Token) leftToken.visitEleExprAST(etemp,o));	
			}
			else if(fAst.e1 instanceof CallExprAST)
			{
				CallExprAST ctemp = (CallExprAST) fAst.e1;
				throw new ForConditionalException((Token) leftToken.visitCallExprAST(ctemp,o));	
			}
			else
			{
				LiteralAST ltemp = (LiteralAST) fAst.e1;
				throw new ForConditionalException( ltemp.literal);
			}
		}
		
		Object e2 = fAst.e2.visit(this,o);
		if(!(e2 instanceof IntegerType || e2 instanceof Integer))
		{
			if(fAst.e2 instanceof BinExprAST)
			{
				BinExprAST btemp = (BinExprAST) fAst.e2;
				throw new ForConditionalException((Token) leftToken.visitBinExprAST(btemp,o));
			}
			else if(fAst.e2 instanceof UnaryExprAST)
			{
				UnaryExprAST utemp = (UnaryExprAST) fAst.e2;
				throw new ForConditionalException((Token) leftToken.visitUnaryExprAST(utemp,o));				
			}
			else if(fAst.e2 instanceof VarExprAST)
			{
				VarExprAST vtemp = (VarExprAST) fAst.e2;
				throw new ForConditionalException((Token) leftToken.visitVarExprAST(vtemp,o));	
			}
			else if(fAst.e2 instanceof EleExprAST)
			{
				EleExprAST etemp = (EleExprAST) fAst.e2;
				throw new ForConditionalException((Token) leftToken.visitEleExprAST(etemp,o));	
			}
			else if(fAst.e2 instanceof CallExprAST)
			{
				CallExprAST ctemp = (CallExprAST) fAst.e2;
				throw new ForConditionalException((Token) leftToken.visitCallExprAST(ctemp,o));	
			}
			else
			{
				LiteralAST ltemp = (LiteralAST) fAst.e1;
				throw new ForConditionalException(ltemp.literal);
			}
		}
		
		fAst.o.visit(this,o);
		return null;
	}

	// WhileStmtAST
	public Object visitWhileStmtAST(WhileStmtAST wAst, Object o)
			throws CompilationException {
		flagLoop = 1;
		Object condi = wAst.e.visit(this,o);
		if(!(condi instanceof BooleanType))
			throw new WhileConditionalException((Token) leftToken.visitWhileStmtAST(wAst,o));
		wAst.o.visit(this,o);
		flagLoop = 0;
		return null;
	}

	// BreakStmtAST
	public Object visitBreakStmtAST(BreakStmtAST ast, Object o)
			throws CompilationException {
		if(flagLoop != 1)
			throw new BreakNotInLoopException((Token) leftToken.visitBreakStmtAST(ast,o));
		return null;
	}

	// CompStatAST
	public Object visitCompStatAST(CompStatAST cAst, Object o)
			throws CompilationException {
		sym.enterScope();
		cAst.v.visit(this,o);
		cAst.s.visit(this,o);
		sym.exitScope();
		return null;
	}

	// ContStmtAST
	public Object visitContStmtAST(ContStmtAST ast, Object o)
			throws CompilationException {
		if(flagLoop != 1)
			throw new ContinueNotInLoopException((Token) leftToken.visitContStmtAST(ast,o));
		return null;
	}

	public Object visitLvalueAST(LvalueAST ast, Object o)
			throws CompilationException {
			/*	System.out.println(""+ast.name.Lexeme);
				System.out.println("f"+flag);
				System.out.println("s" + arr.size());*/
		SymEntry s = sym.lookup(ast.name.Lexeme);
		SymHashEntry tempHash = (SymHashEntry) s;
		if(s == null)
		{
			throw new UndeclaredVariableException((Token) leftToken.visitLvalueAST(ast,o));
		}
		else if(s != null)
		{
			lArray = 1;
			Object varA =  ast.e.visit(this, o);//xet ExprList co hay khong	
			if(varA == null && tempHash.getType() instanceof ArrayType)
			{
				throw new ReferArrayAsVarException((Token) leftToken.visitLvalueAST(ast,o));
			}		
			else if(s.getType() instanceof ArrayType)
			{
				ArrayType temparr = (ArrayType) s.getType();
				if(!(varA instanceof Integer || varA instanceof IntegerType))
					throw new IndexNotIntTypeException((Token) leftToken.visitExprListAST(ast.e,o));
				else if(varA instanceof Integer)
				{
					Integer index = (Integer) varA;
					RangeType typeRa = (RangeType) temparr.iType;
					if(index.intValue() > typeRa.upper || index.intValue() < typeRa.lower)
						throw new IndexOutOfBoundException((Token) leftToken.visitLvalueAST(ast,o));
				}
				else if(varA instanceof IntegerType)
				{
					return temparr.eType;
				}				
			}		
			else if(s.getType() instanceof FunctionType)
			{
				FunctionType ft = (FunctionType) s.getType();
				//System.out.println(sym.getScope(tempHash.getLexeme())+" : "+sym.currentScope);
				if(func == 1)
				{
					if(varA != null)
					{
						throw new ReferProcAsArrayException((Token) leftToken.visitLvalueAST(ast,o));
					}	
					else	
						throw new ReferProcAsVarException((Token) leftToken.visitLvalueAST(ast,o));
				}
				else if(varA != null)
				{
					throw new ReferProcAsArrayException((Token) leftToken.visitLvalueAST(ast,o));
				} 		
				else
				{				
					if((ft.rType) instanceof VoidType)
					{
						throw new ReferProcAsVarException((Token) leftToken.visitLvalueAST(ast,o));
					}
					else if(!(sym.inFunctionScope(tempHash.getLexeme()) || sym.getIdRoot(tempHash.getLexeme())))
							throw new ReferProcAsVarException((Token) leftToken.visitLvalueAST(ast,o));
						else
							return ft.rType;					
				}
								
			}
			else if(s.getType() instanceof ConstType)
			{
				throw new AssignValueToConstantException((Token) leftToken.visitLvalueAST(ast,o));
			}
			else
			{
				if(varA != null)
					throw new ReferVarAsArrayException(ast.name);
				else
					return s.getType();
			}		
		}						
		return null;
	}

	// LiteralAST
	public Object visitIntLiteralAST(IntLiteralAST ast, Object o)
			throws CompilationException {
		if(flag == 1)
		{
			arr.add(i,new IntegerType());
			i++;
		}				
		tyLiter = new IntegerType();
		return Integer.valueOf(ast.literal.Lexeme);
	}

	public Object visitRealLiteralAST(RealLiteralAST ast, Object o)
			throws CompilationException {
		if(flag == 1)
		{
			arr.add(i,new RealType());
			i++;
		}
		tyLiter = new RealType();
		return new RealType();
	}

	public Object visitBoolLiteralAST(BoolLiteralAST ast, Object o)
			throws CompilationException {
		if(flag == 1)
		{
			arr.add(i,new BooleanType());
			i++;
		}
		tyLiter = new BooleanType();
		return new BooleanType();
	}

	public Object visitStringLiteralAST(StringLiteralAST ast, Object o)
			throws CompilationException {
		o = new StringType();
		return new StringType();
	}

	// UnaryExprAST
	public Object visitUnaryExprAST(UnaryExprAST ast, Object o) throws CompilationException {
		Object v = ast.e.visit(this,o);
		if (v != null && v instanceof Integer) 
		{
			Integer iv = (Integer) v;
			if (ast.op.Kind == Token.ADDOP) {
				if (ast.op.Lexeme.equals("-"))
					return new Integer(-(iv.intValue()));
				else
					return v;
			} else
				throw new TypeMismatchException(leftoken(ast,o));
		}
		else if(v != null && v instanceof IntegerType)
		{
			if (ast.op.Kind == Token.ADDOP) {
				return new IntegerType();
			} else
				throw new TypeMismatchException(leftoken(ast,o));
		}
		else if (v != null && v instanceof RealType) 
		{			
			if (ast.op.Kind == Token.ADDOP) 
			{
				return new RealType();
			} 
			else
				throw new TypeMismatchException(leftoken(ast,o));
		}
		else if (v != null && v instanceof BooleanType) 
		{			
			if (ast.op.Kind == Token.NOT) {
				return new BooleanType();
			} else
			{
				throw new TypeMismatchException((Token)leftoken(ast.e,o));					
			}
		}
		else 
			throw new TypeMismatchException(leftoken(ast,o));
	//	return null;
	}

	// BinExprAST
	public Object visitBinExprAST(BinExprAST ast, Object o)
			throws CompilationException {
				
		Object e1 = ast.e1.visit(this, o);
		Object e2 = ast.e2.visit(this, o);
	//	System.out.println(ast.op.Lexeme);
				
		if (e2 instanceof Integer) 
		{
			Integer ie2 = (Integer) e2;
			if (((e1 instanceof ConstType || e1 instanceof Integer) && 
				(ast.op.Kind == Token.MULOP && ast.op.Lexeme.equals("/")) || (ast.op.Kind == Token.DIV) || (ast.op.Kind == Token.MOD)) && (ie2.intValue() == 0)){
				throw new DivideByZeroException(ast.op);
			}
			else if (e1 instanceof Integer) 
			{
				Integer ie1 = (Integer) e1;
				if (ast.op.Kind == Token.ADDOP) 
				{
					if (ast.op.Lexeme.equals("+"))
						return new Integer(ie1.intValue() + ie2.intValue());
					else
						return new Integer(ie1.intValue() - ie2.intValue());
				} 
				else if (ast.op.Kind == Token.MULOP) 
				{
					if (ast.op.Lexeme.equals("*"))
						return new Integer(ie1.intValue() * ie2.intValue());
					else
						return new RealType();
				} 
				else if (ast.op.Kind == Token.DIV) 
				{
					return new Integer(ie1.intValue() / ie2.intValue());
				} 
				else if (ast.op.Kind == Token.MOD) 
				{
					return new Integer(ie1.intValue() % ie2.intValue());
				}
				else if(ast.op.Kind == Token.RELOP || ast.op.Kind == Token.EQOP)
					return new BooleanType();
				else
				{
					if(ast.e2 instanceof BinExprAST)
					{
						BinExprAST btemp = (BinExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitBinExprAST(btemp,o));
					}
					else if(ast.e2 instanceof VarExprAST)
					{
						VarExprAST vtemp = (VarExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitVarExprAST(vtemp,o));
					}
					else if(ast.e2 instanceof UnaryExprAST)
					{
						UnaryExprAST utemp = (UnaryExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitUnaryExprAST(utemp,o));
					}
					else if(ast.e2 instanceof EleExprAST)
					{
						EleExprAST etemp = (EleExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitEleExprAST(etemp,o));
					}
					else if(ast.e2 instanceof CallExprAST)
					{
						CallExprAST ctemp = (CallExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitCallExprAST(ctemp,o));
					}
					else
					{
						LiteralAST ltemp = (LiteralAST) ast.e2;
						throw new TypeMismatchException(ltemp.literal);
					}
				}					
			}
			else if(e1 instanceof IntegerType)
			{
				if(ast.op.Kind == Token.ADDOP || ast.op.Kind == Token.DIV || ast.op.Kind == Token.MOD)
					return new IntegerType();
				else if(ast.op.Kind == Token.MULOP)
				{
					if(ast.op.Lexeme.equals("*"))
						return new IntegerType();
					else //trave cho op : /
						return new RealType();
				}
				else if(ast.op.Kind == Token.RELOP || ast.op.Kind == Token.EQOP)
					return new BooleanType();
				else
				{
					if(ast.e2 instanceof BinExprAST)
					{
						BinExprAST btemp = (BinExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitBinExprAST(btemp,o));
					}
					else if(ast.e2 instanceof VarExprAST)
					{
						VarExprAST vtemp = (VarExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitVarExprAST(vtemp,o));
					}
					else if(ast.e2 instanceof UnaryExprAST)
					{
						UnaryExprAST utemp = (UnaryExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitUnaryExprAST(utemp,o));
					}
					else if(ast.e2 instanceof EleExprAST)
					{
						EleExprAST etemp = (EleExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitEleExprAST(etemp,o));
					}
					else if(ast.e2 instanceof CallExprAST)
					{
						CallExprAST ctemp = (CallExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitCallExprAST(ctemp,o));
					}
					else
					{
						LiteralAST ltemp = (LiteralAST) ast.e2;
						throw new TypeMismatchException(ltemp.literal);
					}
				}
			} 
			else if(e1 instanceof RealType)
			{
				if(ast.op.Kind == Token.ADDOP || ast.op.Kind == Token.MULOP)
					return new RealType();
				else if(ast.op.Kind == Token.RELOP && !(ast.op.Lexeme.equals("<>")))
					return new BooleanType();
				else
				{
					if(ast.e2 instanceof BinExprAST)
					{
						BinExprAST btemp = (BinExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitBinExprAST(btemp,o));
					}
					else if(ast.e2 instanceof VarExprAST)
					{
						VarExprAST vtemp = (VarExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitVarExprAST(vtemp,o));
					}
					else if(ast.e2 instanceof UnaryExprAST)
					{
						UnaryExprAST utemp = (UnaryExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitUnaryExprAST(utemp,o));
					}
					else if(ast.e2 instanceof EleExprAST)
					{
						EleExprAST etemp = (EleExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitEleExprAST(etemp,o));
					}
					else if(ast.e2 instanceof CallExprAST)
					{
						CallExprAST ctemp = (CallExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitCallExprAST(ctemp,o));
					}
					else
					{
						LiteralAST ltemp = (LiteralAST) ast.e2;
						throw new TypeMismatchException(ltemp.literal);
					}
				}
			}
			else
				{
					if(ast.e2 instanceof BinExprAST)
					{
						BinExprAST btemp = (BinExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitBinExprAST(btemp,o));
					}
					else if(ast.e2 instanceof VarExprAST)
					{
						VarExprAST vtemp = (VarExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitVarExprAST(vtemp,o));
					}
					else if(ast.e2 instanceof UnaryExprAST)
					{
						UnaryExprAST utemp = (UnaryExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitUnaryExprAST(utemp,o));
					}
					else if(ast.e2 instanceof EleExprAST)
					{
						EleExprAST etemp = (EleExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitEleExprAST(etemp,o));
					}
					else if(ast.e2 instanceof CallExprAST)
					{
						CallExprAST ctemp = (CallExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitCallExprAST(ctemp,o));
					}
					else
					{
						LiteralAST ltemp = (LiteralAST) ast.e2;
						throw new TypeMismatchException(ltemp.literal);
					}
				}
		}
		else if(e2 instanceof IntegerType)
		{
			if(e1 instanceof IntegerType || e1 instanceof Integer)
			{
				if(ast.op.Kind == Token.ADDOP || ast.op.Kind == Token.DIV || ast.op.Kind == Token.MOD)
					return new IntegerType();
				else if(ast.op.Kind == Token.MULOP)
				{
					if(ast.op.Lexeme.equals("*"))
						return new IntegerType();
					else //tra ve real cho op : /
						return new RealType();
				}
				else if(ast.op.Kind == Token.RELOP || ast.op.Kind == Token.EQOP)
					return new BooleanType();
				else
				{
					if(ast.e2 instanceof BinExprAST)
					{
						BinExprAST btemp = (BinExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitBinExprAST(btemp,o));
					}
					else if(ast.e2 instanceof VarExprAST)
					{
						VarExprAST vtemp = (VarExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitVarExprAST(vtemp,o));
					}
					else if(ast.e2 instanceof UnaryExprAST)
					{
						UnaryExprAST utemp = (UnaryExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitUnaryExprAST(utemp,o));
					}
					else if(ast.e2 instanceof EleExprAST)
					{
						EleExprAST etemp = (EleExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitEleExprAST(etemp,o));
					}
					else if(ast.e2 instanceof CallExprAST)
					{
						CallExprAST ctemp = (CallExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitCallExprAST(ctemp,o));
					}
					else
					{
						LiteralAST ltemp = (LiteralAST) ast.e2;
						throw new TypeMismatchException(ltemp.literal);
					}
				}					
			}
			else if(e1 instanceof RealType)
			{
				if(ast.op.Kind == Token.ADDOP || ast.op.Kind == Token.MULOP)
					return new RealType();
				else if(ast.op.Kind == Token.RELOP && !(ast.op.Lexeme.equals("<>")))
					return new BooleanType();
				else
				{
					if(ast.e2 instanceof BinExprAST)
					{
						BinExprAST btemp = (BinExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitBinExprAST(btemp,o));
					}
					else if(ast.e2 instanceof VarExprAST)
					{
						VarExprAST vtemp = (VarExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitVarExprAST(vtemp,o));
					}
					else if(ast.e2 instanceof UnaryExprAST)
					{
						UnaryExprAST utemp = (UnaryExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitUnaryExprAST(utemp,o));
					}
					else if(ast.e2 instanceof EleExprAST)
					{
						EleExprAST etemp = (EleExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitEleExprAST(etemp,o));
					}
					else if(ast.e2 instanceof CallExprAST)
					{
						CallExprAST ctemp = (CallExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitCallExprAST(ctemp,o));
					}
					else
					{
						LiteralAST ltemp = (LiteralAST) ast.e2;
						throw new TypeMismatchException(ltemp.literal);
					}
				}
			}
			else
				{
					if(ast.e2 instanceof BinExprAST)
					{
						BinExprAST btemp = (BinExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitBinExprAST(btemp,o));
					}
					else if(ast.e2 instanceof VarExprAST)
					{
						VarExprAST vtemp = (VarExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitVarExprAST(vtemp,o));
					}
					else if(ast.e2 instanceof UnaryExprAST)
					{
						UnaryExprAST utemp = (UnaryExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitUnaryExprAST(utemp,o));
					}
					else if(ast.e2 instanceof EleExprAST)
					{
						EleExprAST etemp = (EleExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitEleExprAST(etemp,o));
					}
					else if(ast.e2 instanceof CallExprAST)
					{
						CallExprAST ctemp = (CallExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitCallExprAST(ctemp,o));
					}
					else
					{
						LiteralAST ltemp = (LiteralAST) ast.e2;
						throw new TypeMismatchException(ltemp.literal);
					}
				} 
		}
		else if(e2 instanceof RealType)
		{
			if(e1 instanceof Integer || e1 instanceof NumericType)
			{
				if(ast.op.Kind == Token.ADDOP || ast.op.Kind == Token.MULOP)
					return new RealType();
				else if(ast.op.Kind == Token.RELOP && !(ast.op.Lexeme.equals("<>")))
					return new BooleanType();
				else
				{
					if(ast.e2 instanceof BinExprAST)
					{
						BinExprAST btemp = (BinExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitBinExprAST(btemp,o));
					}
					else if(ast.e2 instanceof VarExprAST)
					{
						VarExprAST vtemp = (VarExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitVarExprAST(vtemp,o));
					}
					else if(ast.e2 instanceof UnaryExprAST)
					{
						UnaryExprAST utemp = (UnaryExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitUnaryExprAST(utemp,o));
					}
					else if(ast.e2 instanceof EleExprAST)
					{
						EleExprAST etemp = (EleExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitEleExprAST(etemp,o));
					}
					else if(ast.e2 instanceof CallExprAST)
					{
						CallExprAST ctemp = (CallExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitCallExprAST(ctemp,o));
					}
					else
					{
						LiteralAST ltemp = (LiteralAST) ast.e2;
						throw new TypeMismatchException(ltemp.literal);
					}
				}
			}				
			else
				{
					if(ast.e2 instanceof BinExprAST)
					{
						BinExprAST btemp = (BinExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitBinExprAST(btemp,o));
					}
					else if(ast.e2 instanceof VarExprAST)
					{
						VarExprAST vtemp = (VarExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitVarExprAST(vtemp,o));
					}
					else if(ast.e2 instanceof UnaryExprAST)
					{
						UnaryExprAST utemp = (UnaryExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitUnaryExprAST(utemp,o));
					}
					else if(ast.e2 instanceof EleExprAST)
					{
						EleExprAST etemp = (EleExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitEleExprAST(etemp,o));
					}
					else if(ast.e2 instanceof CallExprAST)
					{
						CallExprAST ctemp = (CallExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitCallExprAST(ctemp,o));
					}
					else
					{
						LiteralAST ltemp = (LiteralAST) ast.e2;
						throw new TypeMismatchException(ltemp.literal);
					}
				}
		}
		else if(e2 instanceof BooleanType)
		{
			if(e1 instanceof BooleanType)
			{
				if(ast.op.Kind == Token.OR || ast.op.Kind == Token.AND || 
					ast.op.Kind == Token.EQOP || ast.op.Lexeme.equals("<>"))
					return new BooleanType();
				else
				{
					if(ast.e2 instanceof BinExprAST)
					{
						BinExprAST btemp = (BinExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitBinExprAST(btemp,o));
					}
					else if(ast.e2 instanceof VarExprAST)
					{
						VarExprAST vtemp = (VarExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitVarExprAST(vtemp,o));
					}
					else if(ast.e2 instanceof UnaryExprAST)
					{
						UnaryExprAST utemp = (UnaryExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitUnaryExprAST(utemp,o));
					}
					else if(ast.e2 instanceof EleExprAST)
					{
						EleExprAST etemp = (EleExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitEleExprAST(etemp,o));
					}
					else if(ast.e2 instanceof CallExprAST)
					{
						CallExprAST ctemp = (CallExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitCallExprAST(ctemp,o));
					}
					else
					{
						LiteralAST ltemp = (LiteralAST) ast.e2;
						throw new TypeMismatchException(ltemp.literal);
					}
				}
			}				
			else
				{
					if(ast.e2 instanceof BinExprAST)
					{
						BinExprAST btemp = (BinExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitBinExprAST(btemp,o));
					}
					else if(ast.e2 instanceof VarExprAST)
					{
						VarExprAST vtemp = (VarExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitVarExprAST(vtemp,o));
					}
					else if(ast.e2 instanceof UnaryExprAST)
					{
						UnaryExprAST utemp = (UnaryExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitUnaryExprAST(utemp,o));
					}
					else if(ast.e2 instanceof EleExprAST)
					{
						EleExprAST etemp = (EleExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitEleExprAST(etemp,o));
					}
					else if(ast.e2 instanceof CallExprAST)
					{
						CallExprAST ctemp = (CallExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitCallExprAST(ctemp,o));
					}
					else
					{
						LiteralAST ltemp = (LiteralAST) ast.e2;
						throw new TypeMismatchException(ltemp.literal);
					}
				}
		}
		else
				{
					if(ast.e2 instanceof BinExprAST)
					{
						BinExprAST btemp = (BinExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitBinExprAST(btemp,o));
					}
					else if(ast.e2 instanceof VarExprAST)
					{
						VarExprAST vtemp = (VarExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitVarExprAST(vtemp,o));
					}
					else if(ast.e2 instanceof UnaryExprAST)
					{
						UnaryExprAST utemp = (UnaryExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitUnaryExprAST(utemp,o));
					}
					else if(ast.e2 instanceof EleExprAST)
					{
						EleExprAST etemp = (EleExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitEleExprAST(etemp,o));
					}
					else if(ast.e2 instanceof CallExprAST)
					{
						CallExprAST ctemp = (CallExprAST) ast.e2;
						throw new TypeMismatchException((Token)leftToken.visitCallExprAST(ctemp,o));
					}
					else
					{
						LiteralAST ltemp = (LiteralAST) ast.e2;
						throw new TypeMismatchException(ltemp.literal);
					}
				}			
//		return null;
	}

	// VarExprAST
	public Object visitVarExprAST(VarExprAST ast, Object o)
			throws CompilationException {
//		System.out.println(ast.name.Lexeme);		
		SymEntry s = sym.lookup(ast.name.Lexeme);
		if(s == null)
		{
			throw new UndeclaredVariableException(ast.name);
		}
		else if(s != null)
		{
			if(s.getType() instanceof FunctionType)
			{
				//if(sym.getIdRoot(s.getLexeme()))
					throw new ReferProcAsVarException(ast.name);
			}
			else if(s.getType() instanceof ArrayType)
			{
				if(flag == 1)
				{
					arr.add(s.getType());
				}
				else
					throw new ReferArrayAsVarException(ast.name);
			}
			else if(s.getType() instanceof ConstType)
			{
				ConstType cotype = (ConstType) s.getType();
				if(((IntegerType)cotype.Typekind) instanceof IntegerType)
				{
					if(flag == 1)
					{
						arr.add(i,new IntegerType());
						++i;
						return Integer.valueOf(s.getValue());
					}
					return Integer.valueOf(s.getValue());					
				}
				else if(((RealType) cotype.Typekind) instanceof RealType)
				{
					if(flag == 1)
					{
						arr.add(i,new RealType());
						++i;
					}
					return new RealType();
				}
				else if(((BooleanType) cotype.Typekind) instanceof BooleanType)
				{
					if(flag == 1)
					{
						arr.add(i,new BooleanType());
						++i;
					}
					return new BooleanType();
				}
				else
				{
					return new StringType();
				}				
			}
			else //dung cho bien ; flag = 1 la xet cho tham so
			{
				//System.out.println(""+ast.name.Lexeme);
				if(s.getType() instanceof IntegerType)
				{
					if(flag == 1)
					{
						arr.add(i,new IntegerType());
						++i;
					}
					return new IntegerType();
				}
				if(s.getType() instanceof RealType)
				{
					if(flag == 1)
					{
						arr.add(i,new RealType());
						++i;
					}
					return new RealType();
				}
				if(s.getType() instanceof BooleanType)
				{
					if(flag == 1)
					{
						arr.add(i,new BooleanType());
						++i;
					}
					return new BooleanType();
				}
			}
		}
		return null;
	}

	// EleExprAST
	public Object visitEleExprAST(EleExprAST eAst, Object o)
			throws CompilationException {
		SymEntry ele = sym.lookup(eAst.name.Lexeme);
		if(ele == null)
		{
			throw new UndeclaredVariableException(eAst.name);
		}	
		else if(ele != null)
		{
			if(ele.getType() instanceof FunctionType)
			{
				throw new ReferProcAsArrayException(eAst.name);
			}
			else if(!(ele.getType() instanceof ArrayType))
			{
				throw new ReferVarAsArrayException(eAst.name);
			}
			else if(ele.getType() instanceof ArrayType)
			{
				if(flag == 1)
					flag = 2;
				Object tempExpr = eAst.e.visit(this, o);
				if(flag == 2)
					flag = 1;
				if(flag == 1)
					arr.add(tempExpr);				
				
				if(tempExpr == null)
					throw new ReferArrayAsVarException(eAst.name);
				else if(!(tempExpr instanceof IntegerType || tempExpr instanceof Integer))
				{
					if(eAst.e instanceof BinExprAST)
					{
						BinExprAST btemp = (BinExprAST) eAst.e;
						throw new IndexNotIntTypeException((Token) leftToken.visitBinExprAST(btemp,o));
					}
					else if(eAst.e instanceof UnaryExprAST)
					{
						UnaryExprAST utemp = (UnaryExprAST)eAst.e;
						throw new IndexNotIntTypeException((Token) leftToken.visitUnaryExprAST(utemp,o));				
					}
					else if(eAst.e instanceof VarExprAST)
					{
						VarExprAST vtemp = (VarExprAST) eAst.e;
						throw new IndexNotIntTypeException((Token) leftToken.visitVarExprAST(vtemp,o));	
					}
					else if(eAst.e instanceof EleExprAST)
					{
						EleExprAST etemp = (EleExprAST) eAst.e;
						throw new IndexNotIntTypeException((Token) leftToken.visitEleExprAST(etemp,o));	
					}
					else if(eAst.e instanceof CallExprAST)
					{
						CallExprAST ctemp = (CallExprAST) eAst.e;
						throw new IndexNotIntTypeException((Token) leftToken.visitCallExprAST(ctemp,o));	
					}
					else
					{
						LiteralAST ltemp = (LiteralAST) eAst.e;
						throw new IndexNotIntTypeException( ltemp.literal);
					}
				}
					
				
				ArrayType typeTemp = (ArrayType) ele.getType();
				RangeType rantemp = (RangeType) typeTemp.iType;
				int index = Integer.parseInt("" + tempExpr);
				if(index < rantemp.lower || index > rantemp.upper)
					throw new IndexOutOfBoundException(eAst.name);
				return typeTemp.eType;
			}
		}		
		return null;
	}

	// CallExprAST
	public Object visitCallExprAST(CallExprAST cAst, Object o) throws CompilationException{
		 int temp = flagNest++;
		SymEntry idfunc = sym.lookup(cAst.name.Lexeme);
		if(idfunc == null)
		{
			throw new UndeclaredProcedureException(cAst.name);
		}
		else if(idfunc != null )
		{
			if(idfunc.getType() instanceof ArrayType)
			{
				throw new ReferArrayAsProcException(cAst.name);
			}
			else if( !(idfunc.getType() instanceof FunctionType) && sym.inFunctionScope(cAst.name.Lexeme))
			{
				throw new ReferVarAsProcException(cAst.name);
			}
			else if(idfunc.getType() instanceof FunctionType)
			{
				flag = 1;//bat dau lay tham so				
				cAst.e.visit(this, o);
				FunctionType func = (FunctionType) idfunc.getType();
				ParaType pas = (ParaType) func.pType;
				System.out.println("flag "+flag +" " + pas.pa.size() + " arr: " +arr.size()+ " " + i);
												
				if(arr.size() > pas.pa.size())
				{
					throw new TooManyArgumentsException(cAst.name);
				}
				else if(arr.size() < pas.pa.size())
				{
					throw new TooFewArgumentsException(cAst.name);
				}
				
				ParaType paCall = new ParaType(arr);
				
				if(pas.isLeftCompatibleTo(paCall) == false)
					throw new TypeMismatchException((Token) leftToken.visitExprListAST(cAst.e,o));
				
				if(!arr.isEmpty())
				{
					arr.clear();
					i = 0;
				}
				flagNest = temp;
				if(flagNest > 1)
				{
					arr.add( ((FunctionType) idfunc.getType()).rType);
					i++;
					flagNest--;
				}
				return func.rType;
			}			
		}		
		return null;		
	}
	
	Token leftoken(ExprAST ast,Object o) throws CompilationException
	{
		if(ast instanceof UnaryExprAST)
		{
			left++;
			if(left > 1)
			{
				if(ast instanceof BinExprAST)
			{
				BinExprAST btemp = (BinExprAST) ast;
				return (Token) leftToken.visitBinExprAST(btemp,o);
			}
			else if(ast instanceof UnaryExprAST)
			{
				UnaryExprAST utemp = (UnaryExprAST) ast;
				return (Token) leftToken.visitUnaryExprAST(utemp,o);				
			}
			else if(ast instanceof VarExprAST)
			{
				VarExprAST vtemp = (VarExprAST) ast;
				return (Token) leftToken.visitVarExprAST(vtemp,o);	
			}
			else if(ast instanceof EleExprAST)
			{
				EleExprAST etemp = (EleExprAST) ast;
				return (Token) leftToken.visitEleExprAST(etemp,o);	
			}
			else if(ast instanceof CallExprAST)
			{
				CallExprAST ctemp = (CallExprAST) ast;
				return (Token) leftToken.visitCallExprAST(ctemp,o);	
			}
			else
			{
				LiteralAST ltemp = (LiteralAST) ast;
				return ltemp.literal;
			}
			}
			UnaryExprAST temp = (UnaryExprAST) ast;
			return leftoken(temp.e,o);
		}
		else
		{
			if(ast instanceof BinExprAST)
			{
				BinExprAST btemp = (BinExprAST) ast;
				return (Token) leftToken.visitBinExprAST(btemp,o);
			}
			else if(ast instanceof UnaryExprAST)
			{
				UnaryExprAST utemp = (UnaryExprAST) ast;
				return (Token) leftToken.visitUnaryExprAST(utemp,o);				
			}
			else if(ast instanceof VarExprAST)
			{
				VarExprAST vtemp = (VarExprAST) ast;
				return (Token) leftToken.visitVarExprAST(vtemp,o);	
			}
			else if(ast instanceof EleExprAST)
			{
				EleExprAST etemp = (EleExprAST) ast;
				return (Token) leftToken.visitEleExprAST(etemp,o);	
			}
			else if(ast instanceof CallExprAST)
			{
				CallExprAST ctemp = (CallExprAST) ast;
				return (Token) leftToken.visitCallExprAST(ctemp,o);	
			}
			else
			{
				LiteralAST ltemp = (LiteralAST) ast;
				return ltemp.literal;
			}
		}			
	}
}